#include <string>
#include <iostream>
#include <sstream>
#include <list>
#include <algorithm>

#include "baseCard.h"
#include "basePlayer.h"
#include "baseToken.h"
#include "baseBoard.h"
#include "baseDeck.h"

class baseBoard;
class baseDeck;

using namespace std;

basePlayer::basePlayer(string aName, string aColor, baseBoard* aBoard, baseDeck* aDeck) : name(aName), color(aColor), board(aBoard), actionsLeft(actionsNum)
{
    cardList.clear();
    tokenList.clear();
}

basePlayer::basePlayer(string aName, string aColor, baseBoard* aBoard, int aTokenNum, baseDeck* aDeck) : name(aName), color(aColor), board(aBoard), actionsLeft(actionsNum), deck(aDeck)
{
    cardList.clear();

    addTokens(aTokenNum);
}

string basePlayer::getName()
{
    return name;
}

void basePlayer::setName(string aName)
{
    name = aName;
}

string basePlayer::getColor()
{
    return color;
}

list<baseCard> basePlayer::getCardList()
{
    return cardList;
}

list<baseToken> basePlayer::getTokenList()
{
    return tokenList;
}

int basePlayer::getActionsLeft()
{
    return actionsLeft;
}

void basePlayer::addActions(int aActions)
{
    actionsLeft += aActions;

    if(actionsLeft < 0)
        actionsLeft = 0;
}

void basePlayer::addCard(baseCard& aCard)
{
    this->cardList.push_back(aCard);
}

void basePlayer::addToken()
{
    baseToken token(getColor(), this);

    tokenList.push_back(token);
}

void basePlayer::addTokens(int aTokens)
{
    for(int i = 0; i<aTokens; i++)
        addToken();
}

string basePlayer::toString()
{
    stringstream out;

    out << getName() << " " << getColor() << " (" << tokenList.size() << ")"<< endl;
    out << "Actions left " << getActionsLeft() << endl;
    out << "Score: " << getScore() << endl;

    out << cardListToString();

    return out.str();
}

void basePlayer::toString(ostream &out)
{
    out << toString();
}

void basePlayer::toString(string &str)
{
    str = str + toString();
}

string basePlayer::cardListToString()
{
    stringstream out;

    list<baseCard>::iterator lit(cardList.begin()), lend(cardList.end());
    for(;lit!=lend;++lit) out << lit->toString();
    out << endl;

    return out.str();
}

void basePlayer::winCard(baseCard& aCard)
{
    // each player get back tokens
    aCard.giveBackTokens();
    // The active player get the card
    receiveCard(aCard);
    // The active player get the card
    board->removeCard(aCard);
    board->addCard(*(deck->draw()));
}

void basePlayer::cardPlayed(baseCard& aCard)
{
    discardCard(aCard);
}

void basePlayer::discardCard(baseCard& aCard)
{
    cardList.remove(aCard);
}

void basePlayer::useToken(baseCard& aCard)
{
    if(tokenList.size() > 0)
    {
        actionDone();

        aCard.receiveToken(tokenList.front());
        tokenList.pop_front();

        if(aCard.getName().compare("") != 0
           && aCard.isPayed(getColor()))
        {
            winCard(aCard);
        }
    }
}

void basePlayer::takeToken(baseCard& aCard)
{
    actionDone();

    list<baseToken>::iterator lit(aCard.getTokenList().begin()), lend(aCard.getTokenList().end());
    for(;lit!=lend;++lit)
    {
        if(0 == lit->getColor().compare(getColor()))
        {
            receiveToken(*lit);
            aCard.getTokenList().remove(*lit);
            break;
        }
    }
}

void basePlayer::receiveToken(baseToken& aToken)
{
    tokenList.push_back(aToken);
}

void basePlayer::receiveCard(baseCard& aCard)
{
    aCard.setOwner(this);
    cardList.push_back(aCard);
}

void basePlayer::actionDone()
{
    if(actionsLeft > 0)
        actionsLeft--;
}

void basePlayer::giveAllActions()
{
    actionsLeft = actionsNum;
}

void basePlayer::display()
{
    cout << toString();
}

int basePlayer::getScore()
{
    int res = 0;

    list<baseCard>::iterator lit(cardList.begin()), lend(cardList.end());
    for(;lit!=lend;++lit)
    {
        res += lit->getPoint();
    }

    return res;
}
